using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.JsonWebTokens;
using Stickers.Common.Authorization;
using Stickers.Common.DataAccess;
using Stickers.DataAccess.PostgreSQL;
using Stickers.WebApi;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers(options =>
{
    options.InputFormatters.Insert(0, Utils.GetJsonPatchInputFormatter());
    options.SuppressAsyncSuffixInActionNames = false;
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// CORS
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy", pb =>
        {
            pb.AllowAnyOrigin();
            pb.AllowAnyHeader();
            pb.AllowAnyMethod();
        });
    });
}

//
builder.Services.AddRouting(options =>
{
    options.LowercaseUrls = true;
    options.LowercaseQueryStrings = true;
});

var dbConnectionString = builder.Configuration["db:connectionString"];
if (string.IsNullOrWhiteSpace(dbConnectionString))
    throw new ApplicationException("The database connection string is missing.");

builder.Services.AddSingleton<ISimplifiedDataAccessor, PostgreSqlDataAccessor>(_ =>
    new PostgreSqlDataAccessor(dbConnectionString));

var authority = builder.Configuration["keycloak:authority"];
if (string.IsNullOrWhiteSpace(authority))
    throw new ApplicationException("The keycloak authority is missing.");

var realm = builder.Configuration["keycloak:realm"];
if (string.IsNullOrWhiteSpace(realm))
    throw new ApplicationException("The keycloak realm is missing.");

if (!bool.TryParse(builder.Configuration["keycloak:requireHttpsMetadata"], out var requireHttpsMetadata))
{
    requireHttpsMetadata = false;
}

if (!bool.TryParse(builder.Configuration["keycloak:validateIssuer"], out var validateIssuer))
{
    validateIssuer = false;
}

if (!bool.TryParse(builder.Configuration["keycloak:validateAudience"], out var validateAudience))
{
    validateAudience = false;
}

var nameClaimType = builder.Configuration["keycloak:nameClaimType"];
if (string.IsNullOrWhiteSpace(nameClaimType))
{
    nameClaimType = "preferred_username";
}

authority = authority.TrimEnd('/');

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = $"{authority}/realms/{realm}";
        options.MetadataAddress = 
            $"{authority}/realms/{realm}/.well-known/openid-configuration";
        options.RequireHttpsMetadata = requireHttpsMetadata;
        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            NameClaimType = nameClaimType,
            RoleClaimType = ClaimTypes.Role,
            ValidateIssuer = validateIssuer,
            ValidateAudience = validateAudience
        };
        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = async context =>
            {
                if (context is { Principal.Identity: ClaimsIdentity claimsIdentity } and
                    { SecurityToken: JsonWebToken jwt })
                {
                    var bearerToken = jwt.EncodedToken;
                    var permissionService = context.HttpContext.RequestServices.GetRequiredService<IPermissionService>();
                    var permissionClaims = await permissionService.ReadPermissionClaimsAsync(bearerToken,
                        "public", $"/realms/{realm}/protocol/openid-connect/token");
                    var permissionClaimsList = permissionClaims?.ToList();
                    permissionClaimsList?.ForEach(claim => claimsIdentity.AddClaim(claim));
                }
            }
        };
    });

builder.Services.AddSingleton<IPermissionService, KeycloakPermissionService>();
builder.Services.AddHttpClient("JwtTokenClient", client =>
{
    client.BaseAddress = new Uri(authority);
 
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
    
    app.UseCors("CorsPolicy");
}

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
